/*
 * ChannelTransceiver.cpp
 *
 *  Created on: Nov 2, 2013
 *      Author: vladimir
 */

#include "ChannelTransceiver.hpp"

#include "IpcTranceiverIface.hpp"
#include "MessageInfo.hpp"

#include <map>
#include <sstream>
#include <string>

namespace mq
{

struct ChannelTransceiver::Imp
{
	Imp( IpcTranceiverPtr ipcTranceiver_ );


	IpcTranceiverPtr ipcTranceiver;

	std::map< unsigned int, GenericCbType >  msgTypeToCb;

	MessageInfo msgInfo;
};

ChannelTransceiver::Imp::Imp( IpcTranceiverPtr ipcTranceiver_ ) :
		ipcTranceiver( ipcTranceiver_ ),
		msgTypeToCb(),
		msgInfo()
{
}

ChannelTransceiver::ChannelTransceiver( IpcTranceiverPtr ipcTranceiver ) :
		pimpl( new Imp( ipcTranceiver ) )
{
}

ChannelTransceiver::~ChannelTransceiver()
{
}

void ChannelTransceiver::RegisterMsgHandler(
		const unsigned int id,
		const std::string type,
		const GenericCbType & cb)
{
	const auto msgRegResult = pimpl->msgTypeToCb.insert( std::make_pair( id, cb ) );

	if ( ! msgRegResult.second )
	{
		std::stringstream ss;
		ss << "Failed to register message(" << type << "), because it was already registered.";
		throw ss.str();
	}
}

bool ChannelTransceiver::ReceiveMessage()
{
	char* buffer = reinterpret_cast< char* >( & pimpl->msgInfo );
	const unsigned int size = sizeof( pimpl->msgInfo );

	if ( 0 != pimpl->ipcTranceiver->ReceiveMessage( buffer, size ) )
	{
		const auto cbIt = pimpl->msgTypeToCb.find( pimpl->msgInfo.id );
		if ( pimpl->msgTypeToCb.end() == cbIt )
		{
			std::stringstream ss;
			ss << "A message with invalid id (" << pimpl->msgInfo.id << ") received.";
			throw ss.str();
		}

		cbIt->second( pimpl->msgInfo.buffer.begin() );

		return true;
	}

	return false;
}

void ChannelTransceiver::SendTheMessageToChannel(
		const char* data,
		const unsigned int dataSize )
{
	pimpl->msgInfo.UpdateMessageToSend( 1, data, dataSize );

	pimpl->ipcTranceiver->SendMessage( reinterpret_cast<const char*>(& pimpl->msgInfo), dataSize + sizeof( pimpl->msgInfo.id ) );
}

} /* namespace mq */
